﻿namespace Common.Protocol
{
    using System;
    using System.Collections.Generic;

    public class Protocol
    {
        public static List<Block> Blocks { get; set; }

        public static DateTime StartTime
        {
            get
            {
                return Protocol.Blocks[0].Start;
            }
        }

        public static DateTime EndTime
        {
            get
            {
                return Protocol.Blocks[Protocol.Blocks.Count - 1].End;
            }
        }

        /// <summary>
        /// Finds block by time.
        /// </summary>
        /// <param name="time">Time to find.</param>
        /// <returns>KeyValuePair, where key is a block id and value is position in the list of blocks.</returns>
        public static KeyValuePair<int, int> FindBlockByTime(DateTime time)
        {
            int index = -1;
            for (int i = 0; i < Protocol.Blocks.Count; i++)
            {
                if (Protocol.Blocks[i].Start <= time && Protocol.Blocks[i].End >= time)
                {
                    index = i;
                    break;
                }
            }

            if (index != -1)
            {
                int id = Protocol.Blocks[index].Id;
                return new KeyValuePair<int, int>(id, index);
            }
            else
            {
                int prevIndex = Protocol.Blocks.FindLastIndex(0, b => b.End <= time);
                int nextIndex = prevIndex + 1;

                if (prevIndex == -1)
                {
                    return new KeyValuePair<int, int>(Protocol.Blocks[nextIndex].Id, nextIndex);
                }

                if (nextIndex >= Protocol.Blocks.Count)
                {
                    return new KeyValuePair<int, int>(Protocol.Blocks[prevIndex].Id, prevIndex);
                }


                // prevIndes is => 0 and nextIndex is less then Blocks count
                long millisecondsAfterPrev = (long)(time - Protocol.Blocks[prevIndex].End).TotalMilliseconds;
                long millisecondsBeforeNext = (long)(Protocol.Blocks[nextIndex].Start - time).TotalMilliseconds;
                if (millisecondsAfterPrev < millisecondsBeforeNext)
                {
                    return new KeyValuePair<int, int>(Protocol.Blocks[prevIndex].Id, prevIndex);
                }
                else
                {
                    return new KeyValuePair<int, int>(Protocol.Blocks[nextIndex].Id, nextIndex);
                }
            }
        }

        public static DateTime FindClosestTime(DateTime time)
        {
            foreach (var block in Protocol.Blocks)
            {
                if (block.Start <= time && block.End >= time)
                {
                    return time;
                }
            }

            if (time < Protocol.Blocks[0].Start)
            {
                return Protocol.Blocks[0].Start;
            }

            if (time > Protocol.Blocks[Protocol.Blocks.Count - 1].End)
            {
                return Protocol.Blocks[Protocol.Blocks.Count - 1].End;
            }

            for (int i = 0; i < Protocol.Blocks.Count - 1; i++)
            {
                if (time > Protocol.Blocks[i].End && time < Protocol.Blocks[i + 1].Start)
                {
                    int millisecondsAfterEnd = (int)(time - Protocol.Blocks[i].End).TotalMilliseconds;
                    int millisecondsBeforeStart = (int)(Protocol.Blocks[i + 1].Start - time).TotalMilliseconds;
                    if (millisecondsAfterEnd <= millisecondsBeforeStart)
                    {
                        return Protocol.Blocks[i].End;
                    }
                    else
                    {
                        return Protocol.Blocks[i + 1].Start;
                    }
                }
            }

            return time;
        }
    }
}